<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
 * PHP version 5
 *
 * This source file is subject to version 2.1 of the GNU Lesser General Public
 * License, that is bundled with this package in the file COPYING, available
 * through the world wide web at the following URI:
 * http://www.gnu.org/copyleft/lesser.html.
 *
 * @package    ZNF
 * @subpackage Presentation
 * @author     Alessandro Rossini <http://www.alessandrorossini.org>
 * @author     Graziano Liberati <http://www.liberati.org>
 * @copyright  2004-2007 The ZNF Development Team
 * @license    LGPL License 2.1 <http://www.gnu.org/copyleft/lesser.html>
 * @version    SVN $Id: Translation.php 44 2007-07-22 23:55:30Z evolic $
 * @since      Release 0.5.0
 * @link       http://www.zeronotice.org
 */

require_once('ZNF/Config/AppConfig.php');

/**
 * <i>ZNF_Presentation_Translation</i> class handles the translations. Returns translated strings by a numeric index.
 *
 * You can specify if the \texttt{ZNF_Presentation_Translation} should load
 * translations of a specific package or the global ones, typically used in the
 * whole application. The first parameter of the constructor of
 * \texttt{ZNF_Presentation_Translation} let you to specify the package to use.
 * If you pass a string to this parameter the
 * \texttt{ZNF_Presentation_Translation} object will load translations files
 * from the ``lang'' directory of the specified package. If no string is passed
 * to this parameter the \texttt{ZNF_Presentation_Translation} object will load
 * translations files from the ``lang'' directory of the root of the application.
 *
 * @access     public
 * @package    ZNF
 * @subpackage Presentation
 * @author     Alessandro Rossini <http://www.alessandrorossini.org>
 * @author     Graziano Liberati <http://www.liberati.org>
 * @copyright  2004-2007 The ZNF Development Team
 * @license    LGPL License 2.1 <http://www.gnu.org/copyleft/lesser.html>
 * @version    SVN $Id: Translation.php 44 2007-07-22 23:55:30Z evolic $
 * @since      Release 0.5.0
 * @link       http://www.zeronotice.org
 */
class ZNF_Presentation_Translation
{

    /**
     * The <i>ZNF_Config_AppConfig</i> instance.
     *
     * @access protected
     * @var ZNF_Config_AppConfig $_appConfig
     */
    protected $_appConfig;

    /**
     * The array of the translation.
     *
     * @access protected
     * @var array $_translation
     */
    protected $_translation;

    /**
     * Constructs a new <i>ZNF_Presentation_Translation</i> object.
     *
     * @access public
     * @param string $package Name of the package to which the lang refers, leave
     *                        null this parameter to refer to the application lang
     *                        path
     * @param string $lang Name of the language to get (en, it, etc.), leave null
     *                     this parameter to refer to the default application lang
     */
    public function __construct($package = null, $lang = null)
    {
        $this->_appConfig = ZNF_Config_AppConfig::getInstance();
        $this->_parseLang($package, $lang);
    }

    /**
     * Parses and validates the translation files.
     *
     * For performance reasons implements a caching mechanism that serializes the
     * translations file in an internal structure.
     *
     * @access protected
     * @param string $package Name of the package to which the lang refers, leave
     *                        null this parameter to refer to the application lang
     *                        path
     * @param string $lang Name of the language to get (en, it, etc.), leave null
     *                     this parameter to refer to the default application lang
     */
    protected function _parseLang($package = null, $lang = null)
    {
        $path = $this->_getLangPath($package, $lang);
        $xmlTimestamp = filemtime($path);
        $serializedTimestamp = @filemtime(ZNF::CACHE_DIR . "/{$package}_lang_{$lang}.xml.cache");

        // check the timestamp
        if ($xmlTimestamp <= $serializedTimestamp) {
            $this->_translation = unserialize(file_get_contents(ZNF::CACHE_DIR . "/{$package}_lang_{$lang}.xml.cache"));
        } else {
            // get and validate the default language xml document
            $defaultPath = $this->_getLangPath($package, $this->_appConfig->getLang());
            $translationDefaultXml = DOMDocument::load($defaultPath);

            // quick hack to make ZNF work as a PEAR package, to be improved
            $dirName = dirname(__FILE__);
            $schemaPath = substr($dirName, 0, strpos($dirName, 'Presentation')) . "xsd/znf-translation.xsd";
            if (!$translationDefaultXml->schemaValidate($schemaPath)) {
                $translation = new ZNF_Presentation_Translation('ZNF', $_SESSION['znf']['lang']);
                throw new ZNF_Action_RequestProcessorException($translation->get('errorLoadTranslation'));
            }

            $translationDefaultXpath = new DOMXPath($translationDefaultXml);
            $translationDefaultXpath->registerNamespace('tra', 'http://www.zeronotice.org/ZNF/znf-translation');

            // get the default language items
            $defaultItems = $translationDefaultXpath->query('/tra:znf-translation/tra:item');

            // get and validate the language xml document
            $translationXml = DOMDocument::load($path);

            if (!$translationXml->schemaValidate($schemaPath)) {
                $translation = new ZNF_Presentation_Translation('ZNF', $_SESSION['znf']['lang']);
                throw new ZNF_Action_RequestProcessorException($translation->get('errorLoadTranslation'));
            }

            $translationXpath = new DOMXPath($translationXml);
            $translationXpath->registerNamespace('tra', 'http://www.zeronotice.org/ZNF/znf-translation');
            $translation = array();

            // build the translation array completing with default translation
            foreach ($defaultItems as $current) {
                $code = $current->getAttribute('code');
                $item = $translationXpath->query('/tra:znf-translation/tra:item[@code="' . $code . '"]');

                if ($item->length) {
                    $translation[$code] = $item->item(0)->getAttribute('text');
                } else {
                    $translation[$code] = $current->getAttribute('text');
                }

            }

            file_put_contents(ZNF::CACHE_DIR . "/{$package}_lang_{$lang}.xml.cache", serialize($translation));
            $this->_translation = $translation;
        }
    }

    /**
     * Returns the path of a language file.
     *
     * @access protected
     * @param string $package Name of the package to which the lang refers, leave
     *                        null this parameter to refer to the application lang
     *                        path
     * @param string $lang Name of the language to get (en, it, etc.), leave null
     *                     this parameter to refer to the default application lang
     * @return string
     */
    protected function _getLangPath($package = null, $lang = null)
    {
        $path = null;

        // quick hack to make ZNF work as a PEAR package, to be improved
        if ($package == 'ZNF') {
            $dirName = dirname(__FILE__);
            $path = substr($dirName, 0, strpos($dirName, 'Presentation'));
        } elseif ($package) {
            $path =  ZNF::MODULES_DIR . "/$package/";
        }

        if ($lang) {
            $pathLang = $path . ZNF::LANG_DIR . "/{$lang}.xml";

            if (is_file($pathLang)) {
                return $pathLang;
            }

        }

        $pathDefault = $path . ZNF::LANG_DIR . "/{$this->_appConfig->getLang()}.xml";

        if (!is_file($pathDefault)) {
            // not translated to avoid code looping
            throw new ZNF_Presentation_TranslationException('Error loading default language.');
        }

        return $pathDefault;
    }

    /**
     * Returns the translated message.
     *
     * @access public
     * @param string $code Id of the string to translate
     * @return string
     */
    public function get($code)
    {
        if (isset($this->_translation[$code])) {
            return $this->_translation[$code];
        } else {
            return null;
        }
    }

    /**
     * Destroys the <i>ZNF_Presentation_Translation</i> object.</>
     *
     * @access public
     */
    public function __destruct()
    {
    }

}

/**
 * <i>ZNF_Presentation_TranslationException</i> is the exception type for the <i>ZNF_Presentation_Translation</i> class.
 *
 * <i>ZNF_Presentation_TranslationException</i> extends the <i>Exception</i> class of PHP5.
 *
 * @access     public
 * @package    ZNF
 * @subpackage Presentation
 * @author     Alessandro Rossini <http://www.alessandrorossini.org>
 * @author     Graziano Liberati <http://www.liberati.org>
 * @copyright  2004-2007 The ZNF Development Team
 * @license    LGPL License 2.1 <http://www.gnu.org/copyleft/lesser.html>
 * @version    SVN $Id: Translation.php 44 2007-07-22 23:55:30Z evolic $
 * @since      Release 0.5.0
 * @link       http://www.zeronotice.org
 */
class ZNF_Presentation_TranslationException extends Exception
{
}

?>
